package transaction;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.rmi.*;
import java.rmi.registry.LocateRegistry;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.sun.org.apache.xml.internal.serializer.ElemDesc;

/** 
 * Transaction Manager for the Distributed Travel Reservation System.
 * 
 * Description: toy implementation of the TM
 */

public class TransactionManagerImpl
    extends java.rmi.server.UnicastRemoteObject
    implements TransactionManager {
	
	private Map<Integer, List<ResourceManager>> transRMs = new ConcurrentHashMap<Integer, List<ResourceManager>>();
	private Set<Integer> toAbortTrans=new HashSet<Integer>();
	private Set<Integer> toCommitTrans=new HashSet<Integer>();
	
	private int xid=1;
    
    public static void main(String args[]) {
    	/*
		System.setSecurityManager(new RMISecurityManager());
		*/

    	Properties prop = new Properties();
    	try {
			prop.load(new FileInputStream("conf/ddb.conf"));
		} catch (FileNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		String rmiPort = prop.getProperty("tm.port");
		if (rmiPort == null) {
		    rmiPort = "";
		} else if (!rmiPort.equals("")) {
			try{
				LocateRegistry.createRegistry(Integer.parseInt(rmiPort));
			}
			catch(Exception e)
			{
				e.printStackTrace();
				return;
			}
		    rmiPort = "//localhost:" + rmiPort + "/";
		}
	
		try {
		    TransactionManagerImpl obj = new TransactionManagerImpl();
		    Naming.rebind("//localhost/" + TransactionManager.RMIName, obj);
		    System.out.println("TM bound");
		} 
		catch (Exception e) {
		    System.err.println("TM not bound:" + e);
		    System.exit(1);
		}
    }
    
    public synchronized int start() throws RemoteException{
    	return xid++;
    }
    /**
     * @param xid 提价事物的 ID
     * @return 是否提交成功
     */
    
    public boolean commit(int xid) throws RemoteException, InvalidTransactionException, TransactionAbortedException, TransactionCommitFailedException
    {
    	//检查事物是否存在，不存在抛出无效事物异常
    	if(!transRMs.containsKey(xid)){
    		throw new InvalidTransactionException(xid,"Transation does not exist!");
    	}
    	//将事物添加的提交事物列表中
    	if(!toCommitTrans.contains(xid)){
    		toCommitTrans.add(xid);
    	}
    	List<ResourceManager> rms = transRMs.get(xid);
    	Iterator<ResourceManager> iterator = rms.iterator();
    	ResourceManager rm = null;
    	//调用Resource Manager的prepare进行提交前的准备
    	while(iterator.hasNext())
    	{
    		rm = iterator.next();
    		try {
				if(!rm.prepare(xid))
				{
					return false;
				}
			} catch (RemoteException e) {
				System.err.println(rm.getID() +" prepare failed! caused RemoteException "+e.getMessage());
				if(abort(xid)){
					throw new TransactionAbortedException(xid, "Transation commit failed, and successfully aborted!");
				}
				throw new TransactionAbortedException(xid, "Transation commit failed, and unsuccessfully aborted!");
			}
    	}
    	boolean commitSuccessful=true;
    	rms = transRMs.get(xid);
    	iterator = rms.iterator();
    	//调用 ResourceManager的commit 进行事物提交
    	while(iterator.hasNext())
    	{
    		rm = iterator.next();
    		try {
				rm.commit(xid);
				iterator.remove();
			} catch (InvalidTransactionException e) {
				System.err.println(rm.getID()+" commited failed! "+e.getMessage());
				commitSuccessful=false;
				return false;
			}
    	}
    	//判断所有的ResourceManager是否都提交成功
    	if(!commitSuccessful){
    		throw new TransactionCommitFailedException(xid, "Not all Resource Managers commit successfully");
    	}
    	//判断所有ResourceManager是否都被释放
    	if(rms.isEmpty()){
    		transRMs.remove(xid);
    		toCommitTrans.remove(xid);
    		return true;
    	}
    	return false;
    }
    /**
     * @param xid 事物的id
     * @return 是否终止成功
     * 
     */
    public boolean abort(int xid) throws RemoteException, InvalidTransactionException, TransactionAbortedException
    {
    	//查看事物是否存在，不存在则抛出异常
    	if(!transRMs.containsKey(xid)){
    		throw new InvalidTransactionException(xid,"Transation does not exist");
    	}
    	//将事物添加到终止事物列表中
    	if(!toAbortTrans.contains(xid)){
    		toAbortTrans.add(xid);
    	}
    	boolean abortedSuccess=true;
    	List <ResourceManager> RMs=transRMs.get(xid);
    	Iterator<ResourceManager> iterator=RMs.iterator();
    	//调用事物占用的ResourceManager 撤销修改
    	while(iterator.hasNext()){
    		ResourceManager rm=iterator.next();
    		try {
				rm.abort(xid);
				iterator.remove();
			} catch (Exception e) {
				abortedSuccess=false;
				System.err.println(rm.getID()+" abort "+xid+" failed!");
			}
    		
    	}
    	//判断是否都撤销完成
    	if(!abortedSuccess){
    		throw new TransactionAbortedException(xid, "Transaction Abort failed!");
    	}
    	//判断是否还有 ResourceManager 没有撤销
    	if(RMs.isEmpty()){
    		toAbortTrans.remove(xid);
    		transRMs.remove(xid);
    		return true;
    	}
    	return false;
    }
    
	public void ping() throws RemoteException {
	}
    
	/**
	 * @param xid 事物的id
	 * @param rm 资源管理类
	 * 当 RM类对资源进行修改时调用，当 RM 恢复时也会调用
	 */
	public void enlist(int xid, ResourceManager rm) throws RemoteException {
		if(!transRMs.containsKey(xid))//不含有事物就添加
		{
			List<ResourceManager> rms = new ArrayList<ResourceManager>();
			rms.add(rm);
			transRMs.put(xid, rms);
		
		} else if(toAbortTrans.contains(xid)){//事物在abort队列中，说明rm 从失败中恢复需要重新执行 abort
			List<ResourceManager> rms = transRMs.get(xid);
			try{
				System.out.println(rm.getID()+" recover to abort!");
				rm.abort(xid);
				rms.remove(rm);
			}catch(InvalidTransactionException e){
				System.err.println(rm.getID()+" enlist abort failed! "+ e);
			}
			if(rms.isEmpty()){
				toAbortTrans.remove(xid);
				transRMs.remove(xid);
			}
		}else if(toCommitTrans.contains(xid)){//事物在commit队列中，说明rm 从失败中恢复需要重新执行 commit
			List<ResourceManager> rms = transRMs.get(xid);
			try{
				System.out.println(rm.getID()+" recover to commit!");
				rm.commit(xid);
				rms.remove(rm);
			}catch(InvalidTransactionException e){
				System.err.println(rm.getID()+" enlist commit failed! "+ e);
			}
			if(rms.isEmpty()){
				toCommitTrans.remove(xid);
				transRMs.remove(xid);
			}
		}else{//事物存在且没有被commit 或者 abort
			List<ResourceManager> rms = transRMs.get(xid);
			rms.add(rm);
		}
	}
	
    public TransactionManagerImpl() throws RemoteException {
    }

    public boolean dieNow() 
	throws RemoteException {
		System.exit(1);
		return true; // We won't ever get here since we exited above;
		             // but we still need it to please the compiler.
    }

}
